package models

import (
	"errors"
	"mime/multipart"
	"strings"
	"toy-car/pkg/upload"

	"gorm.io/gorm"
)

type ExaFileUploadAndDownload struct {
	ModelBase
	Name string `json:"name" gorm:"comment:文件名"` // 文件名
	Url  string `json:"url" gorm:"comment:文件地址"` // 文件地址
	Tag  string `json:"tag" gorm:"comment:文件标签"` // 文件标签
	Key  string `json:"key" gorm:"comment:编号"`   // 编号
}

type ExaFile struct {
	ModelBase
	FileName     string
	FileMd5      string
	FilePath     string
	ExaFileChunk []ExaFileChunk
	ChunkTotal   int
	IsFinish     bool
}

// file chunk struct, 切片结构体
type ExaFileChunk struct {
	ModelBase
	ExaFileID       uint
	FileChunkNumber int
	FileChunkPath   string
}

//@function: UploadFile
//@description: 根据配置文件判断是文件上传到本地或者七牛云
//@param: header *multipart.FileHeader, noSave string
//@return: err error, file model.ExaFileUploadAndDownload

func UploadFile(header *multipart.FileHeader, noSave string) (err error, file ExaFileUploadAndDownload) {
	oss := upload.NewOss("local")
	filePath, key, uploadErr := oss.UploadFile(header)
	if uploadErr != nil {
		panic(err)
	}
	if noSave == "0" {
		s := strings.Split(header.Filename, ".")
		f := ExaFileUploadAndDownload{
			Url:  filePath,
			Name: header.Filename,
			Tag:  s[len(s)-1],
			Key:  key,
		}
		return Upload(f), f
	}
	return
}

//@function: Upload
//@description: 创建文件上传记录
//@param: file model.ExaFileUploadAndDownload
//@return: error

func Upload(file ExaFileUploadAndDownload) error {
	return db.Create(&file).Error
}

//@function: FindFile
//@description: 删除文件切片记录
//@param: id uint
//@return: error, model.ExaFileUploadAndDownload

func FindFile(id uint) (error, ExaFileUploadAndDownload) {
	var file ExaFileUploadAndDownload
	err := db.Where("id = ?", id).First(&file).Error
	return err, file
}

//@function: GetFileRecordInfoList
//@description: 分页获取数据
//@param: info request.PageInfo
//@return: err error, list interface{}, total int64

func GetFileRecordInfoList(page, pageSize int) (err error, list interface{}, total int64) {
	limit := pageSize
	offset := pageSize * (page - 1)
	var fileLists []ExaFileUploadAndDownload
	err = db.Find(&fileLists).Count(&total).Error
	err = db.Limit(limit).Offset(offset).Order("updated_at desc").Find(&fileLists).Error
	return err, fileLists, total
}

//@author: [piexlmax](https://github.com/piexlmax)
//@function: DeleteFile
//@description: 删除文件记录
//@param: file model.ExaFileUploadAndDownload
//@return: err error

func DeleteFile(fileID int) (err error) {
	var fileFromDb ExaFileUploadAndDownload
	err, fileFromDb = FindFile(uint(fileID))
	oss := upload.NewOss("local")
	if err = oss.DeleteFile(fileFromDb.Key); err != nil {
		return errors.New("文件删除失败")
	}
	err = db.Where("id = ?", fileID).Unscoped().Delete(&ExaFileUploadAndDownload{}).Error
	return err
}

//@author: [piexlmax](https://github.com/piexlmax)
//@function: FindOrCreateFile
//@description: 上传文件时检测当前文件属性，如果没有文件则创建，有则返回文件的当前切片
//@param: fileMd5 string, fileName string, chunkTotal int
//@return: err error, file model.ExaFile

func FindOrCreateFile(fileMd5 string, fileName string, chunkTotal int) (err error, file ExaFile) {
	var cfile ExaFile
	cfile.FileMd5 = fileMd5
	cfile.FileName = fileName
	cfile.ChunkTotal = chunkTotal

	if errors.Is(db.Where("file_md5 = ? AND is_finish = ?", fileMd5, true).First(&file).Error, gorm.ErrRecordNotFound) {
		err = db.Where("file_md5 = ? AND file_name = ?", fileMd5, fileName).Preload("ExaFileChunk").FirstOrCreate(&file, cfile).Error
		return err, file
	}
	cfile.IsFinish = true
	cfile.FilePath = file.FilePath
	err = db.Create(&cfile).Error
	return err, cfile
}

//@author: [piexlmax](https://github.com/piexlmax)
//@function: CreateFileChunk
//@description: 创建文件切片记录
//@param: id uint, fileChunkPath string, fileChunkNumber int
//@return: error

func CreateFileChunk(id uint, fileChunkPath string, fileChunkNumber int) error {
	var chunk ExaFileChunk
	chunk.FileChunkPath = fileChunkPath
	chunk.ExaFileID = id
	chunk.FileChunkNumber = fileChunkNumber
	err := db.Create(&chunk).Error
	return err
}

//@function: DeleteFileChunk
//@description: 删除文件切片记录
//@param: fileMd5 string, fileName string, filePath string
//@return: error

func DeleteFileChunk(fileMd5 string, fileName string, filePath string) error {
	var chunks []ExaFileChunk
	var file ExaFile
	err := db.Where("file_md5 = ? AND file_name = ?", fileMd5, fileName).First(&file).Update("IsFinish", true).Update("file_path", filePath).Error
	err = db.Where("exa_file_id = ?", file.ID).Delete(&chunks).Unscoped().Error
	return err
}
